/*
 * PROJECT:     ReactOS API tests
 * LICENSE:     MIT (https://spdx.org/licenses/MIT)
 * PURPOSE:     Tests for sin
 * COPYRIGHT:   Copyright 2021 Timo Kreuzer <timo.kreuzer@reactos.org>
 */

#if !defined(_CRTBLD) && !defined(_M_IX86)
#define _CRTBLD // we don't want inline sin!
#endif
#include "math_helpers.h"

#ifdef _MSC_VER
#pragma function(sin)
#endif

#if !defined(_M_IX86)
#define HAS_SINF
#elif (defined(TEST_UCRTBASE) || defined(TEST_STATIC_CRT))
#define HAS_LIBM_SSE2
#endif


// These are expected to match exactly
static TESTENTRY_DBL s_sin_exact_tests[] =
{
    { 0x0000000000000000 /*  0.000000 */, 0x0000000000000000 /*  0.000000 */ },
    { 0x8000000000000000 /* -0.000000 */, 0x8000000000000000 /* -0.000000 */ },
    { 0x7ff0000000000000 /*  1.#INF00 */, 0xfff8000000000000 /* -1.#IND00 */ },
    { 0x7ff0000000000001 /*  1.#SNAN0 */, 0x7ff8000000000001 /*  1.#QNAN0 */ },
    { 0x7ff7ffffffffffff /*  1.#SNAN0 */, 0x7fffffffffffffff /*  1.#QNAN0 */ },
    { 0x7ff8000000000000 /*  1.#QNAN0 */, 0x7ff8000000000000 /*  1.#QNAN0 */ },
    { 0x7ff8000000000001 /*  1.#QNAN0 */, 0x7ff8000000000001 /*  1.#QNAN0 */ },
    { 0x7fffffffffffffff /*  1.#QNAN0 */, 0x7fffffffffffffff /*  1.#QNAN0 */ },
    { 0xfff0000000000000 /* -1.#INF00 */, 0xfff8000000000000 /* -1.#IND00 */ },
    { 0xfff0000000000001 /* -1.#SNAN0 */, 0xfff8000000000001 /* -1.#QNAN0 */ },
    { 0xfff7ffffffffffff /* -1.#SNAN0 */, 0xffffffffffffffff /* -1.#QNAN0 */ },
    { 0xfff8000000000000 /* -1.#IND00 */, 0xfff8000000000000 /* -1.#IND00 */ },
    { 0xfff8000000000001 /* -1.#QNAN0 */, 0xfff8000000000001 /* -1.#QNAN0 */ },
    { 0xffffffffffffffff /* -1.#QNAN0 */, 0xffffffffffffffff /* -1.#QNAN0 */ },
};

void Test_sin_exact(void)
{
    for (int i = 0; i < _countof(s_sin_exact_tests); i++)
    {
        double x = u64_to_dbl(s_sin_exact_tests[i].x);
        double z = sin(x);
        ok_eq_dbl_exact("sin", s_sin_exact_tests[i].x, z, s_sin_exact_tests[i].result);
    }
}

// This table is autogenerated by `python gen_math_tests.py sin`
static TESTENTRY_DBL_APPROX s_sin_approx_tests[] =
{
//  {    x,                     {    y_rounded,               y_difference           } }
#ifndef _M_IX86 // Too inaccurate on x86
    {   -0x1.eadfb4c5d390cp+14, {    0x1.116a342b4b4adp-41,   -0x1.a76956bed73a6p-96 }, 1 }, // sin(-31415.926535897932) == 4.8568235395684898392e-13
    {   -0x1.ec19dd8b70e4fp+14, {   -0x1.7491b6f32423ep-40,   -0x1.2ba47a9e0a04cp-94 }, 1 }, // sin(-31494.466352237676) == -1.3236316854728350853e-12
    {   -0x1.ed5406510e393p+14, {   -0x1.9f91ac2f5d5dap-40,    0x1.824696b31adf8p-95 }, 1 }, // sin(-31573.006168577424) == -1.4763977901028917651e-12
    {   -0x1.ee8e2f16ab8d6p+14, {    0x1.676a1ea3bdbe4p-41,   -0x1.5a886e7279359p-96 }, 1 }, // sin(-31651.545984917168) == 6.3844845858690566372e-13
    {   -0x1.efc857dc48e19p+14, {    0x1.c13c6c5cfcfb0p-43,   -0x1.3df13fa2de368p-98 }, 1 }, // sin(-31730.08580125691) == 1.9950087292908043763e-13
    {   -0x1.f10280a1e635cp+14, {   -0x1.24042a691e1dep-40,    0x1.f9810e73931f5p-96 }, 1 }, // sin(-31808.625617596656) == -1.037450204445066539e-12
    {   -0x1.f23ca967838a0p+14, {   -0x1.f01f38b96363ap-40,   -0x1.d1c2e5a4d6981p-95 }, 1 }, // sin(-31887.165433936403) == -1.7625792711306603114e-12
    {   -0x1.f376d22d20de3p+14, {    0x1.04429bdbe4e52p-40,    0x1.5362a2a7d81e5p-94 }, 1 }, // sin(-31965.705250276147) == 9.2462993961467421001e-13
    {   -0x1.f4b0faf2be326p+14, {   -0x1.865fefe6666a7p-44,   0x1.0e16dcfb19275p-101 }, 1 }, // sin(-32044.24506661589) == -8.6680608098688108667e-14
    {    -0x1.3a28c59d5433bp+8, {   -0x1.1b19140c0c0d5p-49,  -0x1.2f92fec660b71p-108 }, 1 }, // sin(-314.1592653589793) == -1.9643867237284719452e-15
#endif
    {    -0x1.395fb5c2b2124p+8, {     0x1.6a09e667f3c64p-1,   -0x1.7c6f2e231f3bcp-57 }, 1 }, // sin(-313.37386719558185) == 0.70710678118656432679
    {    -0x1.3896a5e80ff0ep+8, {     0x1.0000000000000p+0,   -0x1.1246ba5b92c38p-95 }, 1 }, // sin(-312.58846903218443) == 1.0
    {    -0x1.37cd960d6dcf7p+8, {     0x1.6a09e667f3b58p-1,   -0x1.c97a4411f1a5bp-55 }, 1 }, // sin(-311.80307086878696) == 0.70710678118653453353
#ifndef _M_IX86 // Too inaccurate on x86
    {    -0x1.37048632cbae1p+8, {    0x1.cb2fce8fc81a5p-47,   0x1.706d8e920cf32p-101 }, 1 }, // sin(-311.01767270538954) == 1.2744992440044710051e-14
#endif
    {    -0x1.363b7658298cap+8, {    -0x1.6a09e667f3c1fp-1,   -0x1.f5869d7ffc97ap-56 }, 1 }, // sin(-310.23227454199207) == -0.70710678118655670375
    {    -0x1.3572667d876b4p+8, {    -0x1.0000000000000p+0,    0x1.a0ea8fca416bep-93 }, 1 }, // sin(-309.44687637859465) == -1.0
    {    -0x1.34a956a2e549dp+8, {    -0x1.6a09e667f3b9cp-1,   -0x1.b94d434aa36edp-56 }, 1 }, // sin(-308.6614782151972) == -0.70710678118654215657
#ifndef _M_IX86 // Too inaccurate on x86
    {    -0x1.33e046c843287p+8, {   -0x1.a7ccac0e4698bp-46,   0x1.10c2046cbb7e2p-100 }, 1 }, // sin(-307.87608005179976) == -2.3525598156360948157e-14
    {    -0x1.921fb54442d18p+1, {   -0x1.1a62633145c07p-53,   0x1.f1976b7ed8fbdp-109 }, 1 }, // sin(-3.141592653589793) == -1.2246467991473531772e-16
#endif
    {    -0x1.83c325a66500ep+1, {    -0x1.ca9b4332d6f6ap-4,    0x1.59cb0c178e33bp-58 }, 1 }, // sin(-3.029392915961586) == -0.11196447610330797582
    {    -0x1.7566960887304p+1, {    -0x1.c7b90e3024586p-3,   -0x1.9f4870b381f2ap-57 }, 1 }, // sin(-2.9171931783333793) == -0.22252093395631451515
    {    -0x1.670a066aa95fap+1, {    -0x1.5234aca69aa00p-2,    0x1.a88fbcbaf5c66p-56 }, 1 }, // sin(-2.8049934407051724) == -0.33027906195516718498
    {    -0x1.58ad76cccb8f0p+1, {    -0x1.bc4c04d71abc3p-2,    0x1.2e0af83451d65p-56 }, 1 }, // sin(-2.6927937030769655) == -0.43388373911755821505
    {    -0x1.4a50e72eedbe6p+1, {    -0x1.106682221cd8bp-1,    0x1.38d966662d1f0p-55 }, 1 }, // sin(-2.5805939654487586) == -0.53203207651533664873
    {    -0x1.3bf457910fedcp+1, {    -0x1.3f3a0e28bedd2p-1,   -0x1.a36a5e483b6a3p-57 }, 1 }, // sin(-2.4683942278205517) == -0.62348980185873360575
    {    -0x1.2d97c7f3321d2p+1, {    -0x1.6a09e667f3bcdp-1,   -0x1.3267a12a5e3d6p-56 }, 1 }, // sin(-2.356194490192345) == -0.70710678118654758935
    {    -0x1.1f3b3855544c8p+1, {    -0x1.904c37505de4cp-1,    0x1.da288b633c79ap-55 }, 1 }, // sin(-2.243994752564138) == -0.78183148246802986325
    {    -0x1.10dea8b7767bep+1, {    -0x1.b185d590d5a45p-1,    0x1.a808ec7b2c6d5p-56 }, 1 }, // sin(-2.131795014935931) == -0.84672419922828421257
    {    -0x1.0282191998ab4p+1, {    -0x1.cd4bca9cb5c71p-1,   -0x1.098e75d9a5344p-56 }, 1 }, // sin(-2.019595277307724) == -0.90096886790241916039
    {    -0x1.e84b12f775b54p+0, {    -0x1.e344ad05d3f86p-1,   -0x1.ecf7c36a66027p-57 }, 1 }, // sin(-1.9073955396795172) == -0.94388333030836758745
    {    -0x1.cb91f3bbba140p+0, {    -0x1.f329c0558e969p-1,   -0x1.df5721ae60cafp-59 }, 1 }, // sin(-1.7951958020513104) == -0.97492791218182362259
    {    -0x1.aed8d47ffe72cp+0, {    -0x1.fcc7d8c64135fp-1,    0x1.e32d5303c65f4p-57 }, 1 }, // sin(-1.6829960644231035) == -0.99371220989324259088
    {    -0x1.921fb54442d18p+0, {    -0x1.0000000000000p+0,   0x1.377ce858a5d48p-109 }, 1 }, // sin(-1.5707963267948966) == -1.0
    {    -0x1.7566960887304p+0, {    -0x1.fcc7d8c64135fp-1,    0x1.ee864e15e824dp-56 }, 1 }, // sin(-1.4585965891666897) == -0.99371220989324257717
    {    -0x1.58ad76cccb8f0p+0, {    -0x1.f329c0558e969p-1,    0x1.bac61ad6f1716p-56 }, 1 }, // sin(-1.3463968515384828) == -0.97492791218182359534
    {    -0x1.3bf457910fedcp+0, {    -0x1.e344ad05d3f86p-1,    0x1.f3a42114f7352p-56 }, 1 }, // sin(-1.2341971139102759) == -0.94388333030836754701
    {    -0x1.1f3b3855544c8p+0, {    -0x1.cd4bca9cb5c71p-1,    0x1.654f43a8e4abdp-55 }, 1 }, // sin(-1.121997376282069) == -0.90096886790241910726
    {    -0x1.0282191998ab4p+0, {    -0x1.b185d590d5a44p-1,   -0x1.a610a2f6f7922p-56 }, 1 }, // sin(-1.009797638653862) == -0.84672419922828414741
    {    -0x1.cb91f3bbba140p-1, {    -0x1.904c37505de4bp-1,    0x1.34d3a4d34299fp-56 }, 1 }, // sin(-0.8975979010256552) == -0.78183148246802978689
    {    -0x1.921fb54442d18p-1, {    -0x1.6a09e667f3bccp-1,   -0x1.7a7fb8d4bd43fp-55 }, 1 }, // sin(-0.7853981633974483) == -0.70710678118654750275
    {    -0x1.58ad76cccb8f0p-1, {    -0x1.3f3a0e28bedd1p-1,   -0x1.eb7dfa4b38bd5p-56 }, 1 }, // sin(-0.6731984257692414) == -0.62348980185873351001
    {    -0x1.1f3b3855544c8p-1, {    -0x1.106682221cd8ap-1,    0x1.ea82f6aa56b04p-56 }, 1 }, // sin(-0.5609986881410345) == -0.53203207651533654504
    {    -0x1.cb91f3bbba140p-2, {    -0x1.bc4c04d71abc1p-2,    0x1.216613f974f3ep-56 }, 1 }, // sin(-0.4487989505128276) == -0.43388373911755810471
    {    -0x1.58ad76cccb8f0p-2, {    -0x1.5234aca69a9fep-2,    0x1.fcdd4547df9dep-56 }, 1 }, // sin(-0.3365992128846207) == -0.33027906195516706939
    {    -0x1.cb91f3bbba140p-3, {    -0x1.c7b90e3024582p-3,   -0x1.a9a71a6b01713p-59 }, 1 }, // sin(-0.2243994752564138) == -0.22252093395631439576
    {    -0x1.cb91f3bbba140p-4, {    -0x1.ca9b4332d6f61p-4,    0x1.b517cc0394c3ap-60 }, 1 }, // sin(-0.1121997376282069) == -0.11196447610330785412
    {                 0x0.0p+0, {                 0x0.0p+0,                 0x0.0p+0 }, 1 }, // sin(0.0) == 0.0
    {     0x1.cb91f3bbba140p-4, {     0x1.ca9b4332d6f61p-4,   -0x1.b517cc0394c3ap-60 }, 1 }, // sin(0.1121997376282069) == 0.11196447610330785412
    {     0x1.cb91f3bbba140p-3, {     0x1.c7b90e3024582p-3,    0x1.a9a71a6b01713p-59 }, 1 }, // sin(0.2243994752564138) == 0.22252093395631439576
    {     0x1.58ad76cccb8f0p-2, {     0x1.5234aca69a9fep-2,   -0x1.fcdd4547df9dep-56 }, 1 }, // sin(0.3365992128846207) == 0.33027906195516706939
    {     0x1.cb91f3bbba140p-2, {     0x1.bc4c04d71abc1p-2,   -0x1.216613f974f3ep-56 }, 1 }, // sin(0.4487989505128276) == 0.43388373911755810471
    {     0x1.1f3b3855544c8p-1, {     0x1.106682221cd8ap-1,   -0x1.ea82f6aa56b04p-56 }, 1 }, // sin(0.5609986881410345) == 0.53203207651533654504
    {     0x1.58ad76cccb8f0p-1, {     0x1.3f3a0e28bedd1p-1,    0x1.eb7dfa4b38bd5p-56 }, 1 }, // sin(0.6731984257692414) == 0.62348980185873351001
    {     0x1.921fb54442d18p-1, {     0x1.6a09e667f3bccp-1,    0x1.7a7fb8d4bd43fp-55 }, 1 }, // sin(0.7853981633974483) == 0.70710678118654750275
    {     0x1.cb91f3bbba140p-1, {     0x1.904c37505de4bp-1,   -0x1.34d3a4d34299fp-56 }, 1 }, // sin(0.8975979010256552) == 0.78183148246802978689
    {     0x1.0282191998ab4p+0, {     0x1.b185d590d5a44p-1,    0x1.a610a2f6f7922p-56 }, 1 }, // sin(1.009797638653862) == 0.84672419922828414741
    {     0x1.1f3b3855544c8p+0, {     0x1.cd4bca9cb5c71p-1,   -0x1.654f43a8e4abdp-55 }, 1 }, // sin(1.121997376282069) == 0.90096886790241910726
    {     0x1.3bf457910fedcp+0, {     0x1.e344ad05d3f86p-1,   -0x1.f3a42114f7352p-56 }, 1 }, // sin(1.2341971139102759) == 0.94388333030836754701
    {     0x1.58ad76cccb8f0p+0, {     0x1.f329c0558e969p-1,   -0x1.bac61ad6f1716p-56 }, 1 }, // sin(1.3463968515384828) == 0.97492791218182359534
    {     0x1.7566960887304p+0, {     0x1.fcc7d8c64135fp-1,   -0x1.ee864e15e824dp-56 }, 1 }, // sin(1.4585965891666897) == 0.99371220989324257717
    {     0x1.921fb54442d18p+0, {     0x1.0000000000000p+0,  -0x1.377ce858a5d48p-109 }, 1 }, // sin(1.5707963267948966) == 1.0
    {     0x1.aed8d47ffe72cp+0, {     0x1.fcc7d8c64135fp-1,   -0x1.e32d5303c65f4p-57 }, 1 }, // sin(1.6829960644231035) == 0.99371220989324259088
    {     0x1.cb91f3bbba140p+0, {     0x1.f329c0558e969p-1,    0x1.df5721ae60cafp-59 }, 1 }, // sin(1.7951958020513104) == 0.97492791218182362259
    {     0x1.e84b12f775b54p+0, {     0x1.e344ad05d3f86p-1,    0x1.ecf7c36a66027p-57 }, 1 }, // sin(1.9073955396795172) == 0.94388333030836758745
    {     0x1.0282191998ab4p+1, {     0x1.cd4bca9cb5c71p-1,    0x1.098e75d9a5344p-56 }, 1 }, // sin(2.019595277307724) == 0.90096886790241916039
    {     0x1.10dea8b7767bep+1, {     0x1.b185d590d5a45p-1,   -0x1.a808ec7b2c6d5p-56 }, 1 }, // sin(2.131795014935931) == 0.84672419922828421257
    {     0x1.1f3b3855544c8p+1, {     0x1.904c37505de4cp-1,   -0x1.da288b633c79ap-55 }, 1 }, // sin(2.243994752564138) == 0.78183148246802986325
    {     0x1.2d97c7f3321d2p+1, {     0x1.6a09e667f3bcdp-1,    0x1.3267a12a5e3d6p-56 }, 1 }, // sin(2.356194490192345) == 0.70710678118654758935
    {     0x1.3bf457910fedcp+1, {     0x1.3f3a0e28bedd2p-1,    0x1.a36a5e483b6a3p-57 }, 1 }, // sin(2.4683942278205517) == 0.62348980185873360575
    {     0x1.4a50e72eedbe6p+1, {     0x1.106682221cd8bp-1,   -0x1.38d966662d1f0p-55 }, 1 }, // sin(2.5805939654487586) == 0.53203207651533664873
    {     0x1.58ad76cccb8f0p+1, {     0x1.bc4c04d71abc3p-2,   -0x1.2e0af83451d65p-56 }, 1 }, // sin(2.6927937030769655) == 0.43388373911755821505
    {     0x1.670a066aa95fap+1, {     0x1.5234aca69aa00p-2,   -0x1.a88fbcbaf5c66p-56 }, 1 }, // sin(2.8049934407051724) == 0.33027906195516718498
    {     0x1.7566960887304p+1, {     0x1.c7b90e3024586p-3,    0x1.9f4870b381f2ap-57 }, 1 }, // sin(2.9171931783333793) == 0.22252093395631451515
    {     0x1.83c325a66500ep+1, {     0x1.ca9b4332d6f6ap-4,   -0x1.59cb0c178e33bp-58 }, 1 }, // sin(3.029392915961586) == 0.11196447610330797582
#ifndef _M_IX86 // Too inaccurate on x86
    {     0x1.921fb54442d18p+1, {    0x1.1a62633145c07p-53,  -0x1.f1976b7ed8fbdp-109 }, 1 }, // sin(3.141592653589793) == 1.2246467991473531772e-16
    {    0x1.88b2f704a940ap+12, {    0x1.2c3beb21e1e21p-42,    0x1.217b77bd64ac4p-96 }, 1 }, // sin(6283.185307179587) == 2.6666140991780149838e-13
#endif
    {    0x1.88bf88025362bp+12, {     0x1.6a09e667f392dp-1,   -0x1.e354fa9daf23ap-55 }, 1 }, // sin(6283.970705342984) == 0.70710678118647291335
    {    0x1.88cc18fffd84dp+12, {     0x1.0000000000000p+0,   -0x1.cda2427328709p-84 }, 1 }, // sin(6284.756103506382) == 1.0
    {    0x1.88d8a9fda7a6ep+12, {     0x1.6a09e667f3a51p-1,   -0x1.781be8d549a88p-56 }, 1 }, // sin(6285.541501669779) == 0.70710678118650536387
#ifndef _M_IX86 // Too inaccurate on x86
    {    0x1.88e53afb51c8fp+12, {    0x1.5fe7612a8446ap-42,    0x1.c3e9f708e1845p-97 }, 1 }, // sin(6286.326899833176) == 3.1255338440659488842e-13
#endif
    {    0x1.88f1cbf8fbeb1p+12, {    -0x1.6a09e667f4164p-1,   -0x1.908f7a0a7296dp-57 }, 1 }, // sin(6287.112297996574) == -0.70710678118670645651
    {    0x1.88fe5cf6a60d2p+12, {    -0x1.0000000000000p+0,    0x1.ae6bd6b51e560p-87 }, 1 }, // sin(6287.897696159971) == -1.0
    {    0x1.890aedf4502f4p+12, {    -0x1.6a09e667f3219p-1,   -0x1.f8f264d590af0p-56 }, 1 }, // sin(6288.683094323369) == -0.70710678118627182071
#ifndef _M_IX86 // Too inaccurate on x86
    {    0x1.89177ef1fa515p+12, {    0x1.3f552891590a4p-46,   0x1.a9a91352fd251p-100 }, 1 }, // sin(6289.468492486766) == 1.7726523041936962692e-14
    {    0x1.7f7ec53a8d491p+22, {   -0x1.eacd7c60e9693p-32,   -0x1.fd0242346ff14p-86 }, 1 }, // sin(6283185.307179586) == -4.4638243627217424013e-10
    {    0x1.812c48f8ba623p+22, {   -0x1.95451ad7bc5a3p-32,    0x1.d47983cf2b88ep-86 }, 1 }, // sin(6310674.242898497) == -3.6859086397941465454e-10
    {    0x1.82d9ccb6e77b5p+22, {   -0x1.3fbcb94e8f4b2p-32,    0x1.a600993a3b7f0p-86 }, 1 }, // sin(6338163.178617408) == -2.9079929168665506894e-10
    {    0x1.8487507514947p+22, {   -0x1.d468af8ac4781p-33,   -0x1.10deca0ed01c5p-87 }, 1 }, // sin(6365652.114336318) == -2.1300771939389548334e-10
    {    0x1.8634d43341ad9p+22, {   -0x1.2957ec786a59fp-33,   -0x1.6db1b413ce3bfp-87 }, 1 }, // sin(6393141.050055229) == -1.3521614710113589774e-10
    {    0x1.87e257f16ec6bp+22, {   -0x1.f91ca59840ef6p-35,    0x1.ac1d71e96503ep-90 }, 1 }, // sin(6420629.98577414) == -5.7424574808376312145e-11
    {    0x1.898fdbaf9bdfdp+22, {    0x1.664ccd624f124p-36,   -0x1.3a1b4a217c2dfp-90 }, 1 }, // sin(6448118.921493051) == 2.0366997484383273455e-11
    {    0x1.8b3d5f6dc8f8fp+22, {    0x1.afb4b97d4800dp-34,   -0x1.08178180655a9p-88 }, 1 }, // sin(6475607.857211961) == 9.8158569777142859055e-11
    {    0x1.8ceae32bf6121p+22, {    0x1.82eb1fd0fe1e8p-33,    0x1.1f25e1a0d32b2p-87 }, 1 }, // sin(6503096.792930872) == 1.7595014206990244465e-10
#endif
};

void Test_sin_approx(void)
{
    for (int i = 0; i < _countof(s_sin_approx_tests); i++)
    {
        double x = s_sin_approx_tests[i].x;
        double expected = s_sin_approx_tests[i].expected.rounded;
        double z = sin(x);
        int64_t error = abs(ulp_error_precise(&s_sin_approx_tests[i].expected, z));
        ok(error <= s_sin_approx_tests[i].max_error,
            "sin(%.17e) = %.17e, expected %.17e, error %I64d ULPs, max %u ULPs\n",
            x, z, expected, error, s_sin_approx_tests[i].max_error);
    }
}

#if defined(HAS_SINF)

// These are expected to match exactly
static TESTENTRY_DBL s_sinf_exact_tests[] =
{
    { 0x00000000 /*  0.000000 */, 0x00000000 /*  0.000000 */ },
    { 0x80000000 /* -0.000000 */, 0x00000000 /*  0.000000 */ },
    { 0x7f800000 /*  1.#INF00 */, 0x00000000 /*  0.000000 */ },
    { 0x7f800001 /*  1.#SNAN0 */, 0x00000000 /*  0.000000 */ },
    { 0x7fBFffff /*  1.#SNAN0 */, 0x00000000 /*  0.000000 */ },
    { 0x7fC00000 /*  1.#QNAN0 */, 0x00000000 /*  0.000000 */ },
    { 0x7fC80001 /*  1.#QNAN0 */, 0x00000000 /*  0.000000 */ },
    { 0x7fFfffff /*  1.#QNAN0 */, 0x00000000 /*  0.000000 */ },
    { 0xff800000 /* -1.#INF00 */, 0x00000000 /*  0.000000 */ },
    { 0xff800001 /* -1.#SNAN0 */, 0x00000000 /*  0.000000 */ },
    { 0xffBfffff /* -1.#SNAN0 */, 0x00000000 /*  0.000000 */ },
    { 0xffC00000 /* -1.#IND00 */, 0x00000000 /*  0.000000 */ },
    { 0xfff80001 /* -1.#QNAN0 */, 0x00000000 /*  0.000000 */ },
    { 0xffffffff /* -1.#QNAN0 */, 0x00000000 /*  0.000000 */ },
};

void Test_sinf_exact(void)
{
    for (int i = 0; i < _countof(s_sinf_exact_tests); i++)
    {
        float x = u64_to_dbl(s_sinf_exact_tests[i].x);
        float z = sinf(x);
        ok_eq_flt_exact("sinf", s_sinf_exact_tests[i].x, z, s_sinf_exact_tests[i].result);
    }
}

#endif // defined(HAS_SINF)

#if defined(HAS_SINF) || defined(HAS_LIBM_SSE2)

static TESTENTRY_DBL_APPROX s_sinf_approx_tests[] =
{
//  {    x,                     {    y_rounded,               y_difference           } }
    {   -0x1.eadfb40000000p+14, {    0x1.8ba71f0e3a165p-11,   -0x1.70113629ddf5bp-66 }, 1 }, // sinf(-31415.92578125) == 0.00075464786075677952685
    {   -0x1.ec19de0000000p+14, {    0x1.d23c6b26f8c5fp-12,   -0x1.162a7dc796091p-66 }, 1 }, // sinf(-31494.466796875) == 0.00044463730813343829752
    {   -0x1.ed54060000000p+14, {    0x1.4438e44f59ec6p-12,    0x1.8f36805440539p-67 }, 1 }, // sinf(-31573.005859375) == 0.00030920241711960802787
    {   -0x1.ee8e300000000p+14, {    0x1.d2a8e13bd4f20p-11,   -0x1.229f6071a6e76p-65 }, 1 }, // sinf(-31651.546875) == 0.00089008271559485107858
    {   -0x1.efc8580000000p+14, {   -0x1.1db8f36a26919p-13,    0x1.30b2679b98bd5p-67 }, 1 }, // sinf(-31730.0859375) == -0.00013624308787003253439
    {   -0x1.f102800000000p+14, {   -0x1.43cc6a2fc7c57p-11,   -0x1.09048dd43bddep-65 }, 1 }, // sinf(-31808.625) == -0.00061759661727823602791
    {   -0x1.f23caa0000000p+14, {   -0x1.30f8eaeef6063p-11,   -0x1.08a549731fc33p-65 }, 1 }, // sinf(-31887.166015625) == -0.00058168856582608807005
    {   -0x1.f376d20000000p+14, {   -0x1.6906f1418cbdap-13,   -0x1.7f370d9ac8583p-67 }, 1 }, // sinf(-31965.705078125) == -0.00017215114535104501115
    {   -0x1.f4b0fa0000000p+14, {    0x1.e57c603338e57p-11,   -0x1.e60e703023937p-68 }, 1 }, // sinf(-32044.244140625) == 0.00092599075869910062486
    {    -0x1.3a28c60000000p+8, {   -0x1.8aaf31422c6c7p-18,   -0x1.016ef78be1ac3p-78 }, 1 }, // sinf(-314.1592712402344) == -5.8812550511198310487e-6
    {    -0x1.395fb60000000p+8, {     0x1.6a098fb558490p-1,    0x1.6fe95435fc8f7p-56 }, 1 }, // sinf(-313.3738708496094) == 0.70710419739420340198
    {    -0x1.3896a60000000p+8, {     0x1.fffffffffdc30p-1,   -0x1.ea40003e55417p-56 }, 1 }, // sinf(-312.5884704589844) == 0.99999999999898212095
    {    -0x1.37cd960000000p+8, {     0x1.6a09d36a26ef3p-1,   -0x1.69eb2d288c106p-56 }, 1 }, // sinf(-311.8030700683594) == 0.70710621519853442943
    {    -0x1.3704860000000p+8, {   -0x1.965d70634a588p-19,    0x1.cb1932b07b185p-73 }, 1 }, // sinf(-311.0176696777344) == -3.027655155603176096e-6
    {    -0x1.363b760000000p+8, {    -0x1.6a0a631607f33p-1,   -0x1.ae0761295f57cp-55 }, 1 }, // sinf(-310.2322692871094) == -0.70711049693998122789
    {    -0x1.3572660000000p+8, {    -0x1.ffffffffc2728p-1,    0x1.9671e8f7a3460p-55 }, 1 }, // sinf(-309.4468688964844) == -0.99999999997200901304
    {    -0x1.34a9560000000p+8, {    -0x1.6a0900092362ep-1,   -0x1.cad7d2bc15e81p-57 }, 1 }, // sinf(-308.6614685058594) == -0.70709991561461095344
    {    -0x1.33e0460000000p+8, {    0x1.908650d298c0fp-17,   -0x1.93135f6804359p-71 }, 1 }, // sinf(-307.8760681152344) == 0.000011936565362085882244
    {    -0x1.921fb60000000p+1, {    0x1.777a5cf72cec6p-24,   -0x1.4f3b49a585f97p-83 }, 1 }, // sinf(-3.1415927410125732) == 8.7422780003724745258e-8
    {    -0x1.83c3260000000p+1, {    -0x1.ca9b38117e9c3p-4,    0x1.68fc5a6affc72p-58 }, 1 }, // sinf(-3.029392957687378) == -0.11196443463987918798
    {    -0x1.7566960000000p+1, {    -0x1.c7b90eb52b914p-3,   -0x1.9d651746363bcp-58 }, 1 }, // sinf(-2.9171931743621826) == -0.22252093782794502568
    {    -0x1.670a060000000p+1, {    -0x1.5234afcc03518p-2,   -0x1.31b3cb84ca78ap-59 }, 1 }, // sinf(-2.8049933910369873) == -0.33027910883613875933
    {    -0x1.58ad760000000p+1, {    -0x1.bc4c0a9b3782ep-2,   -0x1.10572abc31b4ep-57 }, 1 }, // sinf(-2.692793607711792) == -0.43388382503860867995
    {    -0x1.4a50e80000000p+1, {    -0x1.10667f5e027b7p-1,   -0x1.adb824abf74bap-56 }, 1 }, // sinf(-2.580594062805176) == -0.53203199408129977608
    {    -0x1.3bf4580000000p+1, {    -0x1.3f3a0ccdce952p-1,   -0x1.8621568cfc996p-55 }, 1 }, // sinf(-2.4683942794799805) == -0.62348976146976502266
    {    -0x1.2d97c80000000p+1, {    -0x1.6a09e643bc79bp-1,    0x1.ce1842d039e23p-55 }, 1 }, // sinf(-2.356194496154785) == -0.70710677697046559462
    {    -0x1.1f3b380000000p+1, {    -0x1.904c38252caebp-1,    0x1.93c171cdf2d4ap-55 }, 1 }, // sinf(-2.24399471282959) == -0.78183150724211476553
    {    -0x1.10dea80000000p+1, {    -0x1.b185d7174456cp-1,    0x1.2b4b79ff5c297p-55 }, 1 }, // sinf(-2.1317949295043945) == -0.84672424468059889288
    {    -0x1.02821a0000000p+1, {    -0x1.cd4bc90cd6222p-1,   -0x1.6134f8a06b539p-57 }, 1 }, // sinf(-2.0195953845977783) == -0.90096882135100409952
    {    -0x1.e84b120000000p+0, {    -0x1.e344ada94a219p-1,    0x1.10dc02530be69p-56 }, 1 }, // sinf(-1.9073954820632935) == -0.94388334933779836819
    {    -0x1.cb91f40000000p+0, {    -0x1.f329c0372c2e5p-1,    0x1.2af91f3f08cefp-56 }, 1 }, // sinf(-1.7951958179473877) == -0.97492790864461352274
    {    -0x1.aed8d40000000p+0, {    -0x1.fcc7d8e2ea90fp-1,    0x1.7d779369bd61bp-56 }, 1 }, // sinf(-1.6829960346221924) == -0.99371221322988554522
    {    -0x1.921fb60000000p+0, {    -0x1.ffffffffffff7p-1,   -0x1.94849271d08a5p-55 }, 1 }, // sinf(-1.5707963705062866) == -0.99999999999999904466
    {    -0x1.7566960000000p+0, {    -0x1.fcc7d8c45854ep-1,    0x1.5e901ef86a36fp-57 }, 1 }, // sinf(-1.4585965871810913) == -0.99371220967092609593
    {    -0x1.58ad760000000p+0, {    -0x1.f329bffa6a208p-1,   -0x1.647bb2da4175dp-55 }, 1 }, // sinf(-1.346396803855896) == -0.97492790157144874598
    {    -0x1.3bf4580000000p+0, {    -0x1.e344ad4f1bde0p-1,   -0x1.6488aa379b639p-55 }, 1 }, // sinf(-1.2341971397399902) == -0.94388333883938106396
    {    -0x1.1f3b380000000p+0, {    -0x1.cd4bca52aa014p-1,   -0x1.fb71a3dbd6dfep-55 }, 1 }, // sinf(-1.121997356414795) == -0.90096885928233177938
    {    -0x1.02821a0000000p+0, {    -0x1.b185d685ffaffp-1,    0x1.55ed6ab27bdb3p-55 }, 1 }, // sinf(-1.0097976922988892) == -0.84672422776915808496
    {    -0x1.cb91f40000000p-1, {    -0x1.904c377aef330p-1,   -0x1.8dd20ab4cd4dbp-56 }, 1 }, // sinf(-0.8975979089736938) == -0.78183148742355081875
    {    -0x1.921fb60000000p-1, {    -0x1.6a09e6ecb41fep-1,    0x1.40cbf3c69bd5cp-56 }, 1 }, // sinf(-0.7853981852531433) == -0.70710679664085749822
    {    -0x1.58ad760000000p-1, {    -0x1.3f3a0d88a158fp-1,    0x1.3bec709b29aaep-55 }, 1 }, // sinf(-0.673198401927948) == -0.62348978321885958248
    {    -0x1.1f3b380000000p-1, {    -0x1.106681d9dcbffp-1,    0x1.dc76c4d837f7ep-55 }, 1 }, // sinf(-0.5609986782073975) == -0.53203206810428566417
    {    -0x1.cb91f40000000p-2, {    -0x1.bc4c05149dcc5p-2,    0x1.3d2fb569bd68cp-56 }, 1 }, // sinf(-0.4487989544868469) == -0.43388374269802580331
    {    -0x1.58ad760000000p-2, {    -0x1.5234abe54d203p-2,    0x1.a8ca62750d19ap-57 }, 1 }, // sinf(-0.336599200963974) == -0.33027905070346734687
    {    -0x1.cb91f40000000p-3, {    -0x1.c7b90e72b40eep-3,   -0x1.37f95f54f9decp-58 }, 1 }, // sinf(-0.22439947724342346) == -0.22252093589350558235
    {    -0x1.cb91f40000000p-4, {    -0x1.ca9b4376aefc5p-4,    0x1.2706a5ede05f6p-58 }, 1 }, // sinf(-0.11219973862171173) == -0.11196447709056573816
    {                 0x0.0p+0, {                 0x0.0p+0,                 0x0.0p+0 }, 1 }, // sinf(0.0) == 0.0
    {     0x1.cb91f40000000p-4, {     0x1.ca9b4376aefc5p-4,   -0x1.2706a5ede05f6p-58 }, 1 }, // sinf(0.11219973862171173) == 0.11196447709056573816
    {     0x1.cb91f40000000p-3, {     0x1.c7b90e72b40eep-3,    0x1.37f95f54f9decp-58 }, 1 }, // sinf(0.22439947724342346) == 0.22252093589350558235
    {     0x1.58ad760000000p-2, {     0x1.5234abe54d203p-2,   -0x1.a8ca62750d19ap-57 }, 1 }, // sinf(0.336599200963974) == 0.33027905070346734687
    {     0x1.cb91f40000000p-2, {     0x1.bc4c05149dcc5p-2,   -0x1.3d2fb569bd68cp-56 }, 1 }, // sinf(0.4487989544868469) == 0.43388374269802580331
    {     0x1.1f3b380000000p-1, {     0x1.106681d9dcbffp-1,   -0x1.dc76c4d837f7ep-55 }, 1 }, // sinf(0.5609986782073975) == 0.53203206810428566417
    {     0x1.58ad760000000p-1, {     0x1.3f3a0d88a158fp-1,   -0x1.3bec709b29aaep-55 }, 1 }, // sinf(0.673198401927948) == 0.62348978321885958248
    {     0x1.921fb60000000p-1, {     0x1.6a09e6ecb41fep-1,   -0x1.40cbf3c69bd5cp-56 }, 1 }, // sinf(0.7853981852531433) == 0.70710679664085749822
    {     0x1.cb91f40000000p-1, {     0x1.904c377aef330p-1,    0x1.8dd20ab4cd4dbp-56 }, 1 }, // sinf(0.8975979089736938) == 0.78183148742355081875
    {     0x1.02821a0000000p+0, {     0x1.b185d685ffaffp-1,   -0x1.55ed6ab27bdb3p-55 }, 1 }, // sinf(1.0097976922988892) == 0.84672422776915808496
    {     0x1.1f3b380000000p+0, {     0x1.cd4bca52aa014p-1,    0x1.fb71a3dbd6dfep-55 }, 1 }, // sinf(1.121997356414795) == 0.90096885928233177938
    {     0x1.3bf4580000000p+0, {     0x1.e344ad4f1bde0p-1,    0x1.6488aa379b639p-55 }, 1 }, // sinf(1.2341971397399902) == 0.94388333883938106396
    {     0x1.58ad760000000p+0, {     0x1.f329bffa6a208p-1,    0x1.647bb2da4175dp-55 }, 1 }, // sinf(1.346396803855896) == 0.97492790157144874598
    {     0x1.7566960000000p+0, {     0x1.fcc7d8c45854ep-1,   -0x1.5e901ef86a36fp-57 }, 1 }, // sinf(1.4585965871810913) == 0.99371220967092609593
    {     0x1.921fb60000000p+0, {     0x1.ffffffffffff7p-1,    0x1.94849271d08a5p-55 }, 1 }, // sinf(1.5707963705062866) == 0.99999999999999904466
    {     0x1.aed8d40000000p+0, {     0x1.fcc7d8e2ea90fp-1,   -0x1.7d779369bd61bp-56 }, 1 }, // sinf(1.6829960346221924) == 0.99371221322988554522
    {     0x1.cb91f40000000p+0, {     0x1.f329c0372c2e5p-1,   -0x1.2af91f3f08cefp-56 }, 1 }, // sinf(1.7951958179473877) == 0.97492790864461352274
    {     0x1.e84b120000000p+0, {     0x1.e344ada94a219p-1,   -0x1.10dc02530be69p-56 }, 1 }, // sinf(1.9073954820632935) == 0.94388334933779836819
    {     0x1.02821a0000000p+1, {     0x1.cd4bc90cd6222p-1,    0x1.6134f8a06b539p-57 }, 1 }, // sinf(2.0195953845977783) == 0.90096882135100409952
    {     0x1.10dea80000000p+1, {     0x1.b185d7174456cp-1,   -0x1.2b4b79ff5c297p-55 }, 1 }, // sinf(2.1317949295043945) == 0.84672424468059889288
    {     0x1.1f3b380000000p+1, {     0x1.904c38252caebp-1,   -0x1.93c171cdf2d4ap-55 }, 1 }, // sinf(2.24399471282959) == 0.78183150724211476553
    {     0x1.2d97c80000000p+1, {     0x1.6a09e643bc79bp-1,   -0x1.ce1842d039e23p-55 }, 1 }, // sinf(2.356194496154785) == 0.70710677697046559462
    {     0x1.3bf4580000000p+1, {     0x1.3f3a0ccdce952p-1,    0x1.8621568cfc996p-55 }, 1 }, // sinf(2.4683942794799805) == 0.62348976146976502266
    {     0x1.4a50e80000000p+1, {     0x1.10667f5e027b7p-1,    0x1.adb824abf74bap-56 }, 1 }, // sinf(2.580594062805176) == 0.53203199408129977608
    {     0x1.58ad760000000p+1, {     0x1.bc4c0a9b3782ep-2,    0x1.10572abc31b4ep-57 }, 1 }, // sinf(2.692793607711792) == 0.43388382503860867995
    {     0x1.670a060000000p+1, {     0x1.5234afcc03518p-2,    0x1.31b3cb84ca78ap-59 }, 1 }, // sinf(2.8049933910369873) == 0.33027910883613875933
    {     0x1.7566960000000p+1, {     0x1.c7b90eb52b914p-3,    0x1.9d651746363bcp-58 }, 1 }, // sinf(2.9171931743621826) == 0.22252093782794502568
    {     0x1.83c3260000000p+1, {     0x1.ca9b38117e9c3p-4,   -0x1.68fc5a6affc72p-58 }, 1 }, // sinf(3.029392957687378) == 0.11196443463987918798
    {     0x1.921fb60000000p+1, {   -0x1.777a5cf72cec6p-24,    0x1.4f3b49a585f97p-83 }, 1 }, // sinf(3.1415927410125732) == -8.7422780003724745258e-8
    {    0x1.88b2f80000000p+12, {    0x1.f6ad7e78a0328p-13,   -0x1.aa436861f2a29p-67 }, 1 }, // sinf(6283.185546875) == 0.00023969541122783568232
    {    0x1.88bf880000000p+12, {     0x1.6a09b1c7e68c6p-1,   -0x1.9ae2580560ba3p-56 }, 1 }, // sinf(6283.970703125) == 0.70710521283333414015
    {    0x1.88cc180000000p+12, {     0x1.ffffff0004f68p-1,   -0x1.a259190bc34a5p-57 }, 1 }, // sinf(6284.755859375) == 0.99999997019993446233
    {    0x1.88d8aa0000000p+12, {     0x1.6a09b1579bd62p-1,    0x1.ea71b1703f466p-55 }, 1 }, // sinf(6285.54150390625) == 0.70710519976084312598
    {    0x1.88e53a0000000p+12, {    0x1.f6a3919a4256bp-13,   -0x1.ba2715248779bp-67 }, 1 }, // sinf(6286.32666015625) == 0.00023967692397545575984
    {    0x1.88f1cc0000000p+12, {    -0x1.6a0a85288251ep-1,    0x1.3ae83bb74f359p-57 }, 1 }, // sinf(6287.1123046875) == -0.70711151237006552285
    {    0x1.88fe5c0000000p+12, {    -0x1.ffffff125c732p-1,    0x1.236ef819de659p-57 }, 1 }, // sinf(6287.8974609375) == -0.99999997233519465665
    {    0x1.890aee0000000p+12, {    -0x1.6a08ddf684653p-1,   -0x1.0bd36424c6356p-55 }, 1 }, // sinf(6288.68310546875) == -0.70709890016787327455
    {    0x1.89177e0000000p+12, {   -0x1.e3f4a2574fa69p-13,   -0x1.2146650422a84p-67 }, 1 }, // sinf(6289.46826171875) == -0.0002307680140151869512
    {    0x1.7f7ec60000000p+22, {     0x1.887427b11ac47p-3,   -0x1.dd10e926c57a7p-59 }, 1 }, // sinf(6283185.5) == 0.19162779816847061119
    {    0x1.812c480000000p+22, {    -0x1.ec94333cf07f1p-3,   -0x1.3a03e98470076p-58 }, 1 }, // sinf(6310674.0) == -0.24051704435083779291
    {    0x1.82d9cc0000000p+22, {    -0x1.6bddcd67d016bp-3,   -0x1.8bdb144118f10p-57 }, 1 }, // sinf(6338163.0) == -0.17766914818822541718
    {    0x1.8487500000000p+22, {    -0x1.d34d45f9eba1ap-4,    0x1.2087f6e2c970ap-59 }, 1 }, // sinf(6365652.0) == -0.11408736547921485642
    {    0x1.8634d40000000p+22, {    -0x1.99e1981021928p-5,    0x1.a9152eaf4322ap-60 }, 1 }, // sinf(6393141.0) == -0.050034329412527887796
    {    0x1.87e2580000000p+22, {     0x1.d22323779ecd4p-7,   -0x1.f2a260632928ap-62 }, 1 }, // sinf(6420630.0) == 0.014225380247117426069
    {    0x1.898fdc0000000p+22, {     0x1.413bf7aac99ffp-4,   -0x1.0339b4d304f40p-59 }, 1 }, // sinf(6448119.0) == 0.078426330041658316359
    {    0x1.8b3d600000000p+22, {     0x1.236fed8930f74p-3,   -0x1.6a201ad44011cp-57 }, 1 }, // sinf(6475608.0) == 0.14230332922863519047
    {    0x1.8ceae40000000p+22, {     0x1.a50db19fdc814p-3,   -0x1.b8d3399ce8b32p-57 }, 1 }, // sinf(6503097.0) == 0.20559252518756975152
};

#endif // defined(HAS_SINF) || defined(HAS_LIBM_SSE2)

#if defined(HAS_SINF)

void Test_sinf_approx(void)
{
    for (int i = 0; i < _countof(s_sinf_approx_tests); i++)
    {
        float x = s_sinf_approx_tests[i].x;
        float expected = s_sinf_approx_tests[i].expected.rounded;
        float z = sinf(x);
        int64_t error = abs(ulp_error_flt(expected, z));
        ok(error <= s_sin_approx_tests[i].max_error,
            "sin(%.6e) = %.7e, expected %.7e, error %I64d ULPs, max %u ULPs\n",
            x, z, expected, error, s_sin_approx_tests[i].max_error);
    }
}

#endif // defined(HAS_SINF)

#if defined(HAS_LIBM_SSE2)

__ATTRIBUTE_SSE2__ __m128d __libm_sse2_sin(__m128d Xmm0);

__ATTRIBUTE_SSE2__
void Test_libm_sse2_sin(void)
{
    int i;
    for (i = 0; i < _countof(s_sin_approx_tests); i++)
    {
        double x = s_sin_approx_tests[i].x;
        double expected = s_sin_approx_tests[i].expected.rounded;
        __m128d xmm0 = _mm_set_sd(x);
        __m128d xmm1 = __libm_sse2_sin(xmm0);
        double z = _mm_cvtsd_f64(xmm1);
        int64_t error = ulp_error_precise(&s_sin_approx_tests[i].expected, z);
        ok(error <= s_sin_approx_tests[i].max_error,
            "__libm_sse2_sin(%.17e) = %.17e, expected %.17e, error %I64d ULPs, max %u ULPs\n",
            x, z, expected, error, s_sin_approx_tests[i].max_error);
    }
}

__ATTRIBUTE_SSE2__ __m128 __libm_sse2_sinf(__m128 Xmm0);

__ATTRIBUTE_SSE2__
void Test_libm_sse2_sinf(void)
{
    int i;
    for (i = 0; i < _countof(s_sinf_approx_tests); i++)
    {
        float x = s_sinf_approx_tests[i].x;
        float expected = s_sinf_approx_tests[i].expected.rounded;
        __m128 xmm0 = _mm_set_ps1(x);
        __m128 xmm1 = __libm_sse2_sinf(xmm0);
        float z = _mm_cvtss_f32(xmm1);
        int64_t error = ulp_error_flt(expected, z);
        ok(error <= s_sinf_approx_tests[i].max_error,
            "__libm_sse2_sinf(%.6e) = %.7e, expected %.7e, error %I64d ULPs, max %u ULPs\n",
            x, z, expected, error, s_sinf_approx_tests[i].max_error);
    }
}

#endif // defined(HAS_LIBM_SSE2)

START_TEST(sin)
{
    Test_sin_exact();
    Test_sin_approx();
#if defined(HAS_SINF)
    Test_sinf_exact();
    Test_sinf_approx();
#endif
#if defined(HAS_LIBM_SSE2)
    Test_libm_sse2_sin();
    Test_libm_sse2_sinf();
#endif
}
